package com.adee.jdk8;

import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;

/*
* Stream流，类似于Linux中的pipeline管道，类似于流水线进行数据处理。
* 流必须有一个源，比如Collection接口中的stream()方法返回一个Stream对象，这个Collection对象就是Stream对象的源。
* 流由三部分构成：
* 1. 源；
* 2. 零个或多个中间操作；
* 3. 终止操作。
* 流操作的分类：
* 1. 惰性求值；
* 2. 及早求值。
* 举例：
* stream().xxx().yyy().zzz().count();
* stream()返回源，中间的xxx()、yyy()、zzz()是中间操作，最后的count()。
* 只有调用了count()方法才会执行所有链式调用，如果没有最后的.count()，前面的等于白写，都不会调用，因此说，中间操作为惰性求值，
* 最后的操作为及早求值。
*
* Stream实现原理参考：http://www.javashuo.com/article/p-ssndaurw-dz.html
* 流的中间操作只是用链表方式将每个记录下来，当调用终止操作时才回调中间操作的每个回调函数，比传统遍历效率高，因为中间操作会有中间值。
* 流的串行（单线程）方式：
*   list.stream().map(String::toUpperCase).forEach(System.out::println);
* 流的并行（多线程）方式：
*   list.stream().parallel().map(String::toUpperCase).forEach(System.out::println);
* 流的并行方式实现原理是，根据集合中元素数量计算出分片数，然后启用多个线程，每个线程执行一个分片，计算完后再将各个结果合并。
*
* */
public class Test002_Stream {
    public static void main(String[] args) {
        // test();
        // test1();
        // test2();
        // test3();
        // test4();
        // test5();
        // test6();
        test7();
    }

    /*
    * 创建流的方式：
    * 1. Stream.of(可变参数序列)
    * 2. Stream.of(数组)
    * 3. Arrays.stream(数组)
    * 4. 集合对象.stream()
    * */
    public static void test() {
        Stream<String> stream1 = Stream.of("hello", "world", "hello world");
        String[] arr = {"hello", "world", "hello world"};
        Stream<String> stream2 = Stream.of(arr);
        Stream<String> stream3 = Arrays.stream(arr);
        List<String> list = Arrays.asList("hello", "world", "hello world");
        list.stream().parallel().map(String::toUpperCase).forEach(System.out::println);
    }

    /*
    * IntStream API
    * */
    public static void test1() {
        int[] arr = new int[]{8,2,10,39,1};
        IntStream.of(arr).forEach(System.out::println);
        System.out.println("------");
        IntStream.range(10,20).forEach(System.out::println);
        // map reduce
        int sum = IntStream.of(arr).map(i -> i*2).reduce(0, Integer::sum);
        System.out.println(sum);
    }

    /*
    * stream.toArray
    * */
    public static void test2() {
        Stream<String> stream = Stream.of("hello", "world", "hello world");
        String[] arr = stream.toArray(len -> new String[len]);
        Arrays.asList(arr).forEach(System.out::println);
        System.out.println("------");
        stream = Stream.of("hello", "world", "hello world");
        String[] arr2 = stream.toArray(String[]::new);
        Arrays.asList(arr2).forEach(System.out::println);
    }

    /*
    * stream.collect 返回Collection对象
    * */
    public static void test3() {
        Stream<String> stream = Stream.of("hello", "world", "hello world");
        List<String> list = stream.collect(
                // 创建一个Collection对象
                () -> {
                    System.out.println("创建一个ArrayList对象");
                    return new ArrayList<>();
                },
                // 注册Collection对象如何添加元素的回调函数
                (theList, item) -> {
                    System.out.println("添加一个元素");
                    theList.add(item);
                },
                // 注册添加一个集合对象的回调函数（此处调用不到）
                (theList1, theList2) -> {
                    System.out.println("添加一个集合");
                    theList1.addAll(theList2);
                }); // Collection对象如何merge另一个Collection对象
        list.forEach(System.out::println);
        // 方法引用方式的写法
        Stream<String> stream2 = Stream.of("hello", "world", "hello world");
        List<String> list2 = stream2.collect(LinkedList::new, LinkedList::add, LinkedList::addAll);
        list2.forEach(System.out::println);
        System.out.println("------");
        // Collectors.toList()方法
        Stream<String> stream3 = Stream.of("hello", "world", "hello world");
        List<String> list3 = stream3.collect(Collectors.toList());
        list3.forEach(System.out::println);
    }

    /*
    * stream.flatMap
    * */
    public static void test4() {
        // 求每个数的平方
        List<Integer> list = Arrays.asList(new Integer[]{2,3,5,8});
        list.stream().map(i -> i*i).forEach(System.out::println);
        System.out.println("------");
        // flatMap将多个List转为一个List
        Stream<List<Integer>> listStream = Stream.of(Arrays.asList(new Integer[]{2, 3, 5, 8}),
                Arrays.asList(new Integer[]{9, 7, 1, 6}),
                Arrays.asList(new Integer[]{12, 5, 0, 3}));
        listStream.flatMap(theList -> theList.stream()).forEach(System.out::println);
    }

    /*
    * Stream.iterate() 根据初始种子值和指定规则生成一个序列
    * limit(10) 指定元素个数
    * */
    public static void test5() {
        // 1开始，相差1的等差数列，忽略前10个元素，然后再取出前3个元素，求和
        int sum = Stream.iterate(1, item -> item+1).mapToInt(i -> i).skip(10).limit(3).sum();
        System.out.println(sum); // 36
    }

    /*
    * SummaryStatistics 数据运算
    * */
    public static void test6() {
        IntSummaryStatistics intSummaryStatistics = Stream.iterate(1, i -> i + 1)
                .mapToInt(i -> i).skip(10).limit(5).summaryStatistics();
        double avg = intSummaryStatistics.getAverage();
        long sum = intSummaryStatistics.getSum();
        int min = intSummaryStatistics.getMin();
        int max = intSummaryStatistics.getMax();
        System.out.println("avg: " + avg);
        System.out.println("sum: " + sum);
        System.out.println("min: " + min);
        System.out.println("max: " + max);
    }

    /*
    * 流不可重复使用
    * */
    public static void test7() {
        Stream<Integer> stream = Stream.iterate(1, i -> i + 2).limit(6);
        // stream对象执行了filter就会被标记为已使用，不可再对stream对象使用。调用filter方法返回一个新的stream
//        System.out.println(stream.filter(i -> i > 2));
        // java.lang.IllegalStateException: stream has already been operated upon or closed
        System.out.println(stream.distinct().toArray());
    }
}
